package model;

import model.interfaces.selectable.SelectableCommand;
import model.interfaces.commandTypes.ArgumentsVisitor;
import model.interfaces.commandTypes.MoveArgs;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ListIterator;

/**
 * Command for an army to move in a set path.
 * 
 * @author Ryan S
 * @author Christopher Dudley
 */
class Move implements Command, SelectableCommand, NotPartOfTheSystem, MoveArgs
{
    private static final String name = "Move"; // The name of all move commands.
    private List< Direction > argument; // The direction in which to move.
    private ListIterator< Direction > listIt; // Used to iterate though the
                                              // directions in order.
    
    /**
     * Creates a new move command with no movement directions.
     */
    public Move() {
        argument = new ArrayList< Direction >();
        listIt = argument.listIterator();
    }
    
    /**
     * Creates a new move command with the specified direction.
     * 
     * @param dir
     *            the directions in which to move.
     */
    private Move( List< Direction > dir ) {
        argument = dir;
        listIt = argument.listIterator();
    }
    
    /**
     * Accepts an argument visitor so that the visitor can correctly set the
     * directional argument required by the command.
     * 
     * @param visitor
     *            the ArgumentsVisitor.
     */
    public void accept( ArgumentsVisitor visitor )
    {
        visitor.visitMoveArgs( this );
    }
    
    /**
     * Returns a Command version of itself that can be executed and clears the
     * argument of the current command.
     * 
     * @return a command interface version of this command.
     */
    public Command getCommand()
    {
        Command copy = new Move( argument );
        argument = new ArrayList< Direction >();
        listIt = argument.listIterator();
        return copy;
    }
    
    /**
     * Returns the name of the command.
     * 
     * @return the name of the command.
     */
    public String getName()
    {
        String zeName = new String( name );
        
        if ( argument.size() != 0 )
        {
            zeName.concat( " " + argument.get( 0 ).dir );
            
            int i = 1;
            
            while ( i < argument.size() && i < 3 )
            {
                zeName.concat( ", " + argument.get( i ).dir );
                i++;
            }
            
            if ( argument.size() > 3 ) zeName.concat( ", ..." );
        }
        
        return zeName;
    }
    
    /**
     * Returns whether or not the move command has movement directions left.
     * 
     * @return true if there are more directions in which to move, false if
     *         there are not.
     */
    public boolean hasNext()
    {
        return listIt.hasNext();
    }
    
    public Direction peek()
    {
        Direction dirToMove = listIt.next();
        
        // Rewinde a space to make it a peek.
        listIt.previous();
        
        return dirToMove;
    }
    
    /**
     * Pops the previous direction traveled from the list of directions to
     * travel.
     */
    public void pop()
    {
        listIt.next();
    }
    
    /**
     * Executes the command.
     * 
     * @param player
     *            the player whose army is moving.
     * @param instance
     *            of the army that is moving.
     */
    public void run( Player player, ModelInstance instance )
    {
        player.move( instance, this );
    }
    
    /**
     * Sets the directional argument required by the move command.
     * 
     * @param dir
     *            the directions for the move.
     */
    public void setArgs( Direction[] dir )
    {
        argument = Arrays.asList( dir );
        listIt = argument.listIterator();
    }
    
    /**
     * It's not part of your system!
     */
    public void throwItOnTheGround()
    {
        new ThrowItOnTheGround().cuzMyDadIsNotA( this );
    }
    
}
